/********************************************************
 *	Author: Evgeny Kleiman
 *	Date: October, 2006 
 *  Modified History
 *  9/15/2009 Haiyan Du, HaiyanDu7@gmail.com
 *  This is script originally created by Evgeny for Visual Studio 2005 and later updated by Haiyan Du to fit in Visual Studio 2008, some bugs fixed, added some new features, and etc.
 * 
 * This software is provided "as is", without warranty of any kind, express or 
 * implied, including  but not limited  to the warranties of  merchantability, 
 * fitness for a particular purpose and noninfringement. In no event shall the 
 * authors or  copyright  holders be  liable for any claim,  damages or  other 
 * liability, whether  in an  action of  contract, tort  or otherwise, arising 
 * from,  out of  or in  connection with  the software or  the  use  or  other 
 * dealings in the software.                                                   
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using System.IO;

using System.Collections;
using System.Xml;
using System.Text.RegularExpressions;
using ScriptOutline.Helpers;

namespace ScriptOutline
{
    /// <summary>
    /// This add-in is designed to show structure of client script
    /// of current active document in IDE for Visual Studio
    /// The structure is shown as a tree with the root node Client script,
    /// sub nodes "Script" for each script block of the document
    ///  and subnodes with names of functions (or sub's) for each
    ///  function (sub) of these script blocks (as  corresponding tool
    ///  in Visual Sudio 6).
    /// The add-in works as a tool window. It is called 
    ///  when commmand bar button ScriptOutline is clicked
    /// or by selecting Tools\Add-In Manager and then checking
    /// of a check box ScriptOutline. 
    /// When the tool window is activated and a document is opened in IDE
    ///  then document client script structure will be shown. 
    /// After double clicking on  name of a function or a script node
    /// the focus on the document will be moved to the line
    /// corresponding this node and the line will be selected
    /// The tool also allows to sort list of functions by their names:
    /// for that right click on the tree and choose in context menu
    /// Sort/Alphabetically
    /// 
    /// Limitation:
    /// 1.The program finds all script blocks that are bounded by
    ///  "&lt script &gt" and, optionally, &lt!--, from one side
    ///  and "&lt /script&gt" from the other side with optional --&gt
    /// 2.If file extension is ".js"  (".vbs") the all its content is considered
    ///  as one script block in javascript (vbscript).
    /// 3.A script block  is considered as javascript
    /// by default. If it has attribute language=vbscript or language="vbscript"
    ///  that it is considered as vbscript block, otherwise it is considered
    /// as javascript block. 
    /// 4. Script block having attribute runat=server or runat="server" is ignored
    /// 5. Inline script blocks are ignored. It means that , for example, code
    /// &lt INPUT TYPE="text" onkeydown="do_check('ConvertButton');" 
    /// will be ignored
    /// 6.Changes in active documnet will be shown in the script outline
    /// only when text of the document will be saved
    /// </summary>
    /// <remarks>
    ///              Structure of the program
    /// 1.On event Load of ToolWindowControl the first function
    ///  BuildFunctionsList is calling
    /// 2.BuildFunctionsList gets name of a file corresponding to the active document
    ///  and saves it in private variable _FileFullName
    ///  Then it calls  
    ///  2.1. procedure ReadDocument
    ///  2.2. procedure ParseDocument
    ///  2.3. procedure BuildTree
    /// 3. Procedure ReadDocument reads the file in private ArrayList AllDocument
    ///  (a line is saved as an element of the array list) 
    /// 4.Procedure ParseDocument make all main work by building 
    ///  XMLDocument ScriptStructure that contains all information needed
    ///  for show of the client script structire as a tree.
    /// 5. Procedure BuildTree pass this information to woindow control
    ///  trvScript
    /// 
    ///  All additional information is found in comments of the procedures
    ///  that are called by ParseDocument
    /// </remarks>
    public partial class ToolWindowControl : UserControl
    {
        #region Variables

        const string DefaultLanguage = "JScript";
        const string JavaScriptLanguage = "JScript";
        const string VbscriptLanguage = "VBScript";
        const string JavaScriptExtension = ".js";
        const string VbScriptExtension = ".vbs";

        private ArrayList AllDocument = null;
        private string strAllDocument = "";
        private XmlDocument ScriptStructure = null;
        private string _DocumentFileExtension = "";
        private string _FileFullName = "";
        private bool _canExpand = false;
        private string _txtDoc = "";

        #endregion
        #region Public properties
        /// <summary>
        /// Recieves the VS DTE object
        /// </summary>
        private DTE2 m_dte;			// Reference to the Visual Studio DTE object
        public DTE2 DTE
        {
            get { return m_dte; }
            set
            {
                m_dte = value;
            }
        }
        public TreeView FunctionListTree
        {
            get { return trvScript; }
        }
        #endregion
        #region Public methods
        public ToolWindowControl()
        {
            InitializeComponent();
        }
        public void OnWindowActivated()
        {
            trvScript.Nodes.Clear();
            BuildFunctionsList();
        }
        #endregion

        #region Core functionality
        /// <summary>
        /// The procedure gets name of a file corresponding to the active document
        ///  and saves it in private variable _FileFullName
        ///  and also saves the file extension in private variable _DocumentFileExtension
        ///  Then it calls  
        ///   procedure ReadDocument
        ///   procedure ParseDocument
        ///   procedure BuildTree
        /// </summary>
        public void BuildFunctionsList()
        {
            _txtDoc = "";
            AllDocument = new ArrayList();            
           
            if (m_dte.ActiveWindow.Document != null)
                _FileFullName = m_dte.ActiveWindow.Document.FullName;
            else
            {
                trvScript.Nodes.Clear();
                return;
            }
            _DocumentFileExtension = Path.GetExtension(_FileFullName);
            ReadDocument(_FileFullName);
            ParseDocument();
           
        }
       
        /// <summary>
        /// The procedure is called by BuildFunctionsList
        /// It reads lines of the document,
        /// removes redundant spaces and adds each line to
        /// arraylist AllDocument (global)
        /// Also join of lines as string strAllDocument (global)
        /// </summary>
        /// <param name="FileFullName"></param>
        private void ReadDocument(string FileFullName)
        {
           
            StreamReader sr = File.OpenText(FileFullName);
            if (_DocumentFileExtension == JavaScriptExtension ||
             _DocumentFileExtension == VbScriptExtension)
            {
               _txtDoc = sr.ReadToEnd();
            }
            else
            { string line = "";
            StringBuilder sb = new StringBuilder();
            while ((line = sr.ReadLine()) != null)
            {
                AllDocument.Add(RemoveNotEssentialSpaces(line));
                sb.Append(line);
                //strAllDocument += line;
            }

            sr.Close();
            strAllDocument = sb.ToString();
            if (AllDocument[AllDocument.Count - 1].ToString() == "")
                AllDocument[AllDocument.Count - 1] = "*";
                
            }
            sr.Close();
           
            
        }

        /// <summary>
        /// This function only can be called after Function ReadDocument
        /// 1. Create root of output XML document
        /// 2. Run over lines of AllDocument
        /// 3. Extract current script block and process it 
        ///  by calling procedure ProcessScriptBlok
        /// </summary>
        /// <remarks>
        /// </remarks>
        private void ParseDocument()
        {
            ScriptStructure = new XmlDocument();
            XmlNode root = ScriptStructure.CreateElement("FunctionsStructure");
            ScriptStructure.AppendChild(root);
            if (_DocumentFileExtension == JavaScriptExtension ||
              _DocumentFileExtension == VbScriptExtension)
            {
                ProcessScriptFile();
                AllDocument.Clear();//release memory of this ArrayList
            }
            else
            {
                ProcessWebFile();
              
                BuildSortedTree();
            }  

           
        }//end of ParseDocument           

        private void ProcessScriptFile()
        {
            //2.
            //int lAllDocument = strAllDocument.Length;
            //int LineCounter = AllDocument.Count;

            //int indexScriptBlockBegin = 0;
            //int indexScriptBlockEnd = 0;
            //int indexBeginLine = 0;
            //int indexEndLine = 0;

            //indexEndLine = LineCounter - 1;
            //indexScriptBlockEnd = AllDocument[LineCounter - 1].ToString().Length - 1;
            //ProcessScriptBlok(indexBeginLine, indexEndLine,indexScriptBlockBegin, indexScriptBlockEnd);

          

            //_loadedDocName = Doc.Path + Doc.Name;
            //lbDocName.Text = Doc.Name;

            this.trvScript .BeginUpdate();
            this.trvScript.Nodes.Clear();
            _canExpand = true;
            var nodes = (new ScriptOutline.Parsers.JavascriptParser()).Parse(_txtDoc);
            FillNodes(nodes, trvScript.Nodes);
            trvScript.EndUpdate();
             
        }
        private void FillNodes(Hierachy<CodeNode> source, TreeNodeCollection dest)
        {
            if (source.Childrens == null)
            {
                return;
            }

          
            foreach (var item in source.Childrens)
            {
                CodeNode node = item.Item;
                var caption = !string.IsNullOrEmpty(node.Alias)
                    ? node.Alias
                    : string.Format("Anonymous function at line {0}", node.StartLine);

                TreeNode treeNode = new TreeNode(caption);
                treeNode.Tag = node;
                treeNode.ToolTipText = node.Comment;
                treeNode.StateImageIndex = GetImageIndex(node.Opcode);
                dest.Add(treeNode);

                if (item.HasChildrens)
                {
                    FillNodes(item, treeNode.Nodes);
                }

                treeNode.Expand();
            }
        }
        private int GetImageIndex(string opCode)
        {
            switch (opCode)
            {
                case "Function":
                    return 3;
                case "ObjectLiteral":
                    return 1;
                default:
                    return 3;
            }
        }
        private void ProcessWebFile()
        {
            //*******Constants

            const string BeginScript = "<script";
            const string CloseSharpBracket = ">";
            const string EndScript1 = "/>";
            const string EndScript2 = "</script>";
            const string OpenScriptBracket = "<!--";
            const string CloseScriptBracket = "-->";
            //********************************
            //1.
            ScriptStructure = new XmlDocument();
            //add root
            XmlNode root = ScriptStructure.CreateElement("FunctionsStructure");
            ScriptStructure.AppendChild(root);

            //2.
            int lAllDocument = strAllDocument.Length;
            int LineCounter = AllDocument.Count;
            string line = "";
            int ScriptBlockState = 0;
            //Description of values of ScriptBlockState:
            //0 - search BeginScript
            //1 - beginning is found --> search CloseSharpBracket or EndScript1
            //2 - CloseSharpBracket or EndScript1 is found 
            //  --> search OpenScriptBracket or EndScript2
            //3 - OpenScriptBracket is found --> search CloseScriptBracket
            //4 - CloseScriptBracket is found -- search EndScript2
            //5 - EndScript2 is found -> Process block and return to ScriptBlockState = 0
            int indexScriptBlockBegin = 0;
            int indexScriptBlockEnd = 0;
            int indexBeginLine = 0;
            int indexEndLine = 0;
            int indEndScript2 = 0;
            int i = 0;//iterator line number
            while (i < LineCounter)
            {
                #region
                line = AllDocument[i].ToString();
                switch (ScriptBlockState)
                {
                    case 0: //search BeginScript

                        if (i > indexEndLine)
                            //if the current line is end line of previous block
                            //begin to search from the end of previous script block
                            //otherwise from start of the line
                            indexScriptBlockEnd = 0;
                        if (line.ToLower().IndexOf(BeginScript, indexScriptBlockEnd) >= 0)
                        {
                            indexScriptBlockBegin = line.ToLower().IndexOf(BeginScript, indexScriptBlockEnd);
                            indexBeginLine = i; //save number of line where begin of script was found
                            ScriptBlockState = 1;
                        }
                        else
                        {
                            indexScriptBlockEnd = 0;
                            i += 1; //go to the next line
                        }


                        break;
                    case 1: //search EndScript
                        if (i == indexBeginLine)
                            //search from indexScriptBlockBegin+1
                            indexScriptBlockEnd = indexScriptBlockBegin + 1;
                        else
                            indexScriptBlockEnd = 0;
                        //search for one of two possible close brackets for script
                        int indEndScript1 = line.ToLower().IndexOf(EndScript1, indexScriptBlockEnd);
                        int indCloseSharpBracket = line.ToLower().IndexOf(CloseSharpBracket, indexScriptBlockEnd);
                        if (indEndScript1 < 0 && indCloseSharpBracket < 0)
                        {
                            //pass to the following string
                            indexScriptBlockEnd = 0;
                            i += 1; //go to the next line
                        }
                        else
                        {//found "/>" or ">"
                            if (indEndScript1 >= 0)
                            {
                                //found block <script.../> that is empty script block
                                //its processing is only adding script node to the XML doc
                                indexScriptBlockEnd = indEndScript1;
                                indexEndLine = i;
                                ScriptBlockState = 0;
                                ProcessScriptBlok(indexBeginLine, indexEndLine,
                                    indexScriptBlockBegin, indexScriptBlockEnd);

                            }
                            else //it is not empty script block
                            {
                                indexScriptBlockEnd = indCloseSharpBracket;
                                //pass to finding EndScript2 or OpenScriptBracket
                                ScriptBlockState = 2;
                            }

                        }//************
                        break;
                    case 2:
                        // now we should find EndScript2 or OpenScriptBracket
                        indEndScript2 = line.ToLower().IndexOf(EndScript2, indexScriptBlockEnd);
                        int indOpenScriptBracket = line.ToLower().IndexOf(OpenScriptBracket, indexScriptBlockEnd);
                        if (indEndScript2 < 0 && indOpenScriptBracket < 0)
                        {
                            //pass to the following string
                            indexScriptBlockEnd = 0;
                            i += 1; //go to the next line
                        }
                        else
                        {
                            if (indOpenScriptBracket >= 0)
                            {
                                //we should find CloseScriptBracket and then
                                // EndScript2
                                ScriptBlockState = 3;
                                indexScriptBlockEnd = indOpenScriptBracket + 3;
                            }
                            else
                            {
                                //EndScript2 is found
                                ScriptBlockState = 5;
                                indexScriptBlockEnd = indEndScript2 + 8;
                            }
                        }
                        break;
                    case 3:
                        //search CloseScriptBracket
                        int indCloseScriptBracket = line.ToLower().IndexOf(CloseScriptBracket, indexScriptBlockEnd);
                        if (indCloseScriptBracket < 0)
                        {
                            indexScriptBlockEnd = 0;
                            i += 1;
                        }
                        else
                        {
                            ScriptBlockState = 4;
                            indexScriptBlockEnd = indCloseScriptBracket + 2;
                        }
                        break;
                    case 4://search EndScript2
                        indEndScript2 = line.ToLower().IndexOf(EndScript2, indexScriptBlockEnd);
                        if (indEndScript2 < 0)
                        {
                            indexScriptBlockEnd = 0;
                            i += 1;
                        }
                        else
                        {
                            ScriptBlockState = 5;
                            indexScriptBlockEnd = indEndScript2 + 8;
                        }
                        break;
                    case 5:
                        indexEndLine = i; //save number of line where begin of script was found
                        ScriptBlockState = 0;
                        ProcessScriptBlok(indexBeginLine, indexEndLine,
                            indexScriptBlockBegin, indexScriptBlockEnd);
                        break;
                }//end of switch
                #endregion
            }//while (i < LineCounter)
        }
        private void ProcessWebFile2()
        {
        }


        /// <summary>
        /// The function  should add a script node with all its content
        ///  to XmlDocument ScriptStructure
        /// </summary>
        /// <param name="indexBeginLine">
        /// index of a line in  ArrayList AllDocument where the script block is beginning
        /// </param>
        /// <param name="indexEndLine">
        /// index of a line in  ArrayList AllDocument where the script block is ending
        /// </param>
        /// <param name="indexScriptBlockBegin">
        ///  index of firtst character of the script block
        /// </param>
        /// <param name="indexScriptBlockEnd">
        /// index of last character of the script block
        /// </param>
        /// <remarks>
        ///  1. Firstly check if the script block does not contain 'runat="server"'
        ///  2. Then add script node to ScriptStructure
        ///  3. If script block contains type of language (vbscript,jscript,javascript)
        ///    then add attribute language to the node
        ///  3.Call ClearScriptBlok for removing redundant spaces
        ///   and other auxiliary operations
        ///  4.Parse the block and extract functions from it. 
        ///   For each function
        ///    - depending of script language call FindVBScriptFunction or FindJScriptFunction
        ///    - then call AddFunctionNode
        /// </remarks>
        private void ProcessScriptBlok(int indexBeginLine, int indexEndLine,
                                int indexScriptBlockBegin, int indexScriptBlockEnd)
        {
            //constants
            string runatserver = "runat=\"server\"";
            string runatserver2 = "runat=server";
            string Vbscript1 = "language=\"vbscript\"";
            string Vbscript2 = "language=vbscript";
            string Vbscript3 = "type=\"text/vbscript\"";
            string Vbscript4 = "type=text/vbscript";

            string src = "";
            //string JavaScript1 = "type=\"text/javascript\"";
            //string JavaScript2 = "language=\"javascript\"";
            //string JavaScript3 = "language=\"jscript\"";

            string BlockLanguage = DefaultLanguage;
            ///////////////////////////////////////

            string BeginLine = AllDocument[indexBeginLine].ToString();
            string EndLine = AllDocument[indexEndLine].ToString();
            src = GetJavaScriptSrc(BeginLine);
            if (BeginLine.ToLower().IndexOf(runatserver) < 0 &&
                BeginLine.ToLower().IndexOf(runatserver2) < 0)
            {
                //build string containing all block
                string strAllBlock = "";
                if (indexBeginLine == indexEndLine) //all in one line
                    strAllBlock += BeginLine.Substring(indexScriptBlockBegin,
                         indexScriptBlockEnd - indexScriptBlockBegin + 1);
                else
                {
                    strAllBlock += BeginLine.Substring(indexScriptBlockBegin,
                        BeginLine.Length - indexScriptBlockBegin);
                    for (int i = indexBeginLine + 1; i <= indexEndLine; i++)
                    {
                        strAllBlock += AllDocument[i].ToString();
                    }
                    strAllBlock += EndLine.Substring(0, indexScriptBlockEnd);
                }

                //get root
                XmlNode root = ScriptStructure.DocumentElement;
                //add new script node
                XmlNode ScriptNode = ScriptStructure.CreateElement("script");
                root.AppendChild(ScriptNode);
                //check language

                if (_DocumentFileExtension == JavaScriptExtension)
                    BlockLanguage = JavaScriptLanguage;
                else
                    if (_DocumentFileExtension == VbScriptExtension)
                        BlockLanguage = VbscriptLanguage;
                    else

                        if (strAllBlock.ToLower().IndexOf(Vbscript1) >= 0 ||
                            strAllBlock.ToLower().IndexOf(Vbscript2) >= 0 ||
                            strAllBlock.ToLower().IndexOf(Vbscript3) >= 0 ||
                            strAllBlock.ToLower().IndexOf(Vbscript4) >= 0)
                            BlockLanguage = VbscriptLanguage;

                ////
                //assign this language to attribute
                XmlAttribute attr = ScriptStructure.CreateAttribute("language");
                if (src != "")
                    attr.Value = BlockLanguage + "-" + src;
                else
                    attr.Value = BlockLanguage;
                ScriptNode.Attributes.Append(attr);
                // add attribute line
                attr = ScriptStructure.CreateAttribute("line");
                attr.Value = indexBeginLine.ToString();
                ScriptNode.Attributes.Append(attr);


                //now begin parsing of the script block to extract all 
                //functions name
                //Indication of function block
                // a)vbscript:
                //  [sub, end sub] or [function, end function]
                // b)javascript
                //  [function... {,}]
                //Additional conditions:
                // - ignore not essential spaces (between end and sub,
                //   end and function, function and })
                // - ignore blocks in " " and ' '
                // - for each "left bracket" find right "right bracket"
                // Algorithm
                // 1.For each pattern containing more than one space
                //  replace it to one space
                // 2.For each opening " symbol find  its closing parther
                //  and remove all block between these symbols. 
                //  The same for symbol ' in case of javascript
                // 3. In each line find the most left block // ( for jscript)
                //    or ' (for vb script) and remove it and all part to the right
                //    Also for javascript remove content between /* and*/
                // 4.for each "left function bracket" find right "right bracket"
                // and add a function node to the document
                ////////////////////////////////////////////////////////////
                //1,2
                ClearScriptBlok(BlockLanguage,
                 indexBeginLine, indexEndLine,
                  indexScriptBlockBegin, indexScriptBlockEnd);
                //3.

                //4.
                //input:
                int IndexOfFirstLineForSearch = indexBeginLine;
                int IndexOfFunctionWord = indexScriptBlockBegin;
                //output
                int IndexOfFirstLineOfFunction = 0;
                int IndexOfLastLineOfFunction = 0;
                int IndexOfEndFunction = 0;
                string FunctionName = "";

                bool FunctionIsFound = true;
                switch (BlockLanguage)
                {
                    case VbscriptLanguage:
                        while (FunctionIsFound)
                        {
                            FunctionIsFound =
                                FindVBScriptFunction(IndexOfFirstLineForSearch,
                                    0,
                                    indexEndLine,
                                    out  IndexOfFirstLineOfFunction,
                                    out IndexOfLastLineOfFunction,
                                    out IndexOfEndFunction, out FunctionName);
                            if (FunctionIsFound)
                            {
                                AddFunctionNode(ScriptNode, FunctionName, IndexOfFirstLineOfFunction);
                                //prepare input for the next function
                                IndexOfFirstLineForSearch = IndexOfLastLineOfFunction + 1;

                            }
                        }
                        break;
                    case JavaScriptLanguage:
                        while (FunctionIsFound)
                        {
                            FunctionIsFound =
                                FindJScriptFunction(IndexOfFirstLineForSearch,
                                    IndexOfFunctionWord,
                                    indexEndLine,
                                    out  IndexOfFirstLineOfFunction,
                                    out IndexOfLastLineOfFunction,
                                    out IndexOfEndFunction, out FunctionName);
                        
                            if (FunctionIsFound)
                            {
                                AddFunctionNode(ScriptNode, FunctionName, IndexOfFirstLineOfFunction);
                                //prepare input for the next function
                                IndexOfFirstLineForSearch = IndexOfLastLineOfFunction;
                                IndexOfFunctionWord = IndexOfEndFunction + 1;
                            }
                            //exit from loop if function is not found

                        }
                        break;
                }
            }
        }

        /// <summary>
        /// get the JS Link file name
        /// </summary>
        /// <param name="jstring"> <script src="file name" language="JavaScript" type="text/javascript"></script></param>
        /// <returns></returns>
        private string GetJavaScriptSrc(string jstring)
        { 
            string src = "";                    
            jstring=jstring.ToLower();
            string[] tokens = jstring.Split(new char[]{' '},StringSplitOptions.RemoveEmptyEntries);
            foreach (string token in tokens)
            {
                if (token.StartsWith("src="))
                {
                    int startIndex = token.IndexOf('=');
                    int endIndex=token.LastIndexOf("\"");
                    src = token.Substring(startIndex+1,endIndex-startIndex);
                    src=src.Replace("\"","");
                    break;
                }

            }
            return src;
        }
        /// <summary>
        /// The function is aimed to replace literas and comments to sequencies 
        /// of repeated symbol *. It helps to remove redundant words
        ///  such as "function" and "sub" that are found inside comments
        ///  or literals and do not defined reak functions of the script
        /// Aloso it squeezes spaces to one space 
        ///  
        /// </summary>
        /// <param name="Language"></param>
        /// <param name="indexBeginLine"></param>
        /// <param name="indexEndLine"></param>
        /// <param name="indexScriptBlockBegin"></param>
        /// <param name="indexScriptBlockEnd"></param>
        private void ClearScriptBlok(string Language,
            int indexBeginLine, int indexEndLine,
            int indexScriptBlockBegin, int indexScriptBlockEnd)
        {
            //*************Constants**************
            const string Quote1 = "'";
            const string Quote2 = "\"";
           
            const string LeftCommentJMark = "//";
            const string LeftCommentBracket = "/*";

            const string ReplaceSymbol = "*";

            
            string line = "";
            string Quote = "";
            int indQuote1 = 0;
            int indQuote2 = 0;
            int indQuote = 0;
            int CountToExamine = 0;
            int StartIndex = 0;
            int indBeginSearch = indexScriptBlockBegin;

            int indLeftCommentJMark = 0;
            int indLeftCommentBracket = 0;
            int lReplace = 0;
            string sReplace = "";
            //end line and position for right comment bracket if found
            int indLastLineOfComment = 0;
            int indRightCommentBracket = 0;
            
           
            int i = indexBeginLine;
            while (i <= indexEndLine)
            {
                line = AllDocument[i].ToString();

                if (Language == JavaScriptLanguage)
                    line = RemoveJScriptInternalQuotes(line);
                else
                    line = RemoveVBScriptInternalQuotes(line);
                //common part for both languages
                //remove  all blocks
                // between "" and ''
                //1.Find Quote1 or Quote2 as most left in the line
                CountToExamine = GetCountToExamine(line, i, indexBeginLine,
                    indexEndLine, indBeginSearch, indexScriptBlockEnd, out StartIndex);
                indQuote1 = line.IndexOf(Quote1, StartIndex, CountToExamine);
                indQuote2 = line.IndexOf(Quote2, StartIndex, CountToExamine);

                //compute indQuote as -1, if both indQuote1, indQuote2=-1
                // and as minimal positive from them otherwise
                if (indQuote1 < 0)
                {
                    indQuote = indQuote2;
                    Quote = Quote2;
                }
                else //indQuote1 >= 0
                {
                    if (indQuote2 < 0 || indQuote2 > indQuote1)
                    {
                        indQuote = indQuote1;
                        Quote = Quote1;
                    }
                    else
                    {
                        indQuote = indQuote2;
                        Quote = Quote2;
                    }
                }

                /////////////////////
                switch (Language)
                {
                    case JavaScriptLanguage:

                        //2.Check if there is a left comment mark to the left of the quote
                        indLeftCommentJMark = line.IndexOf(LeftCommentJMark, StartIndex, CountToExamine);
                        indLeftCommentBracket = line.IndexOf(LeftCommentBracket, StartIndex, CountToExamine);

                        if (indQuote < 0 && indLeftCommentJMark < 0 && indLeftCommentBracket < 0)
                        {
                            i += 1;
                            continue;
                        }

                        //case 1: no comments
                        if (indLeftCommentJMark < 0 && indLeftCommentBracket < 0)
                        {
                            if (indQuote >= 0)
                            {
                                //replace text between quotes to *
                                line = RemoveLiterals(line, Quote);
                                AllDocument[i] = line;
                            }
                            continue;
                        }

                        //case 2: left comment found
                        if (indLeftCommentJMark >= 0 && indLeftCommentBracket < 0)
                        {
                            if (indLeftCommentJMark < indQuote || indQuote < 0)
                            {
                                //replace substring of the line beginning from 
                                //indLeftCommentJMark and ending by+CountToExamine
                                //with ***
                                lReplace = CountToExamine - indLeftCommentJMark;
                                if (lReplace > 0)
                                {
                                    sReplace = RepeatSymbol(ReplaceSymbol, lReplace);
                                    ReplaceSubstring(ref line, indLeftCommentJMark, sReplace);
                                }

                            }
                            else
                            {
                                if (indQuote >= 0)
                                {
                                    //replace text between quotes to *
                                    line = RemoveLiterals(line, Quote);
                                }
                            }
                            AllDocument[i] = line;
                            continue;
                        }

                        //case 3: left comment bracket found
                        if (indLeftCommentJMark < 0 && indLeftCommentBracket >= 0)
                        {
                            if (indLeftCommentBracket < indQuote || indQuote < 0)
                            {
                                //  call procedure of remove commentaries
                                // that finds the closing commetary bracket,replaces all text between
                                // brackets to * and increases i to the index of the last line
                                ReplaceBracketComments(i,
                                indexEndLine, indBeginSearch,
                                  indLeftCommentBracket,
                                  out  indLastLineOfComment, out  indRightCommentBracket);
                                i = indLastLineOfComment;
                                indBeginSearch = indRightCommentBracket + 1;
                            }
                            else
                            {
                                if (indQuote >= 0)
                                {
                                    //replace text between quotes to *
                                    line = RemoveLiterals(line, Quote);
                                    AllDocument[i] = line;
                                }
                            }
                            continue;
                        }

                        //case 4: left comment bracket found and left comment mark is founs
                        if (indLeftCommentJMark >= 0 && indLeftCommentBracket >= 0)
                        {
                            if (indLeftCommentJMark < indLeftCommentBracket)
                            {
                                if (indLeftCommentJMark < indQuote || indQuote < 0)
                                {
                                    //replace substring of the line beginning from 
                                    //indLeftCommentJMark and ending by+CountToExamine
                                    //with ***
                                    lReplace = CountToExamine - indLeftCommentJMark;
                                    if (lReplace > 0)
                                    {
                                        sReplace = RepeatSymbol(ReplaceSymbol, lReplace);
                                        ReplaceSubstring(ref line, indLeftCommentJMark, sReplace);
                                    }

                                }
                                else
                                {
                                    if (indQuote >= 0)
                                    {
                                        //replace text between quotes to *
                                        line = RemoveLiterals(line, Quote);
                                    }

                                }
                                AllDocument[i] = line;
                            }
                            else //indLeftCommentBracket <indLeftCommentJMark
                            {
                                if (indLeftCommentBracket < indQuote || indQuote < 0)
                                {
                                    //  call procedure of remove commentaries
                                    // that finds the closing commetary bracket,replaces all text between
                                    // brackets to * and increases i to the index of the last line
                                    ReplaceBracketComments(i,
                                      indexEndLine, indBeginSearch,
                                      indLeftCommentBracket,
                                      out  indLastLineOfComment, out  indRightCommentBracket);
                                    i = indLastLineOfComment;
                                    indBeginSearch = indRightCommentBracket + 1;
                                }
                                else
                                {
                                    if (indQuote >= 0)
                                    {
                                        //replace text between quotes to *
                                        line = RemoveLiterals(line, Quote);
                                        AllDocument[i] = line;

                                    }
                                }
                            }
                        }

                        //////////////////////////////////


                        break;
                    case VbscriptLanguage:
                        //at this point all internal "'" are removed
                        //Also we have indQuote, indQuote1, IndQuote2,CountToExamine
                        //here indQuote is -1, if both indQuote1, indQuote2=-1
                        // and as minimal positive from them otherwise
                        //const string Quote1 = "'";
                        //const string Quote2 = "\"";
                        if (indQuote < 0)
                        {
                            i += 1;
                            continue;
                        }
                        //case 1:  indQuote1<0 , indQuote2>=0: replace literals begin from indQuote
                        //case 2:   indQuote1>=0 , indQuote2<0: replace to the left of indQuote
                        //case 3:  0<=indQuote1<indQuote2 : replace to the left of indQuote
                        //case 4: 0<=indQuote2<indQuote1 :replace literals begin from indQuote

                        //case1,4:
                        if (indQuote1 < 0 ||
                            (0 <= indQuote2 && indQuote2 < indQuote1)
                            )
                        {
                            //replace literals begin from indQuote
                            Quote = "\"";
                            line = RemoveLiterals(line, Quote);
                        }
                        //case 2,3
                        if (indQuote2 < 0 ||
                            (0 <= indQuote1 && indQuote1 < indQuote2)
                            )
                        {
                            //replace to the left of indQuote
                            lReplace = CountToExamine - indQuote;
                            if (lReplace > 0)
                            {
                                sReplace = RepeatSymbol(ReplaceSymbol, lReplace);
                                ReplaceSubstring(ref line, indQuote, sReplace);
                            }
                        }


                        AllDocument[i] = line;
                        //i += 1;
                        break;
                }

            }
        }

        /// <summary>
        /// The procedure replaces repeated spaces or tab symbols with
        ///  one space symbol
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        private string RemoveNotEssentialSpaces(string line)
        {
            string Result = line;
            string DoubleSpace = "  ";
            //firstly replace tab to space
            Result = Result.Replace("\t", " ");
            while (Result.IndexOf(DoubleSpace) >= 0)
            {
                Result = Result.Replace(DoubleSpace, " ");
            }

            return Result;

        }
        /// <summary>
        /// The procedure replaces symbol " or '
        ///  tha is found inside a literal to symbol *
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        /// <remarks>
        /// Internal quote is or \* or \'. So it is replaced to
        /// **
        /// </remarks>
        private string RemoveJScriptInternalQuotes(string line)
        {
            string Result = line;
            string Quote1 = "\\\"";
            string Quote2 = "\\\'";
            Result = Result.Replace(Quote1, "**");
            Result = Result.Replace(Quote2, "**");
            return Result;
        }

        /// <summary>
        /// The procedure replaces double symbol "" to
        ///  double symbol **
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        private string RemoveVBScriptInternalQuotes(string line)
        {
            string Result = line;
            string Quote1 = "\"\"";

            Result = Result.Replace(Quote1, "**");

            return Result;
        }

        /// <summary>
        /// Removes all blocks between 2 quotes (include quotes)
        /// and replaces them with strings the same length
        ///  comprising of symbol *
        /// </summary>
        /// <param name="line"></param>
        /// <param name="Quote"></param>
        /// <returns></returns>
        private string RemoveLiterals(string line, string Quote)
        {
            //string Quote1 = "\"";
            string Result = line;
            while (Result.IndexOf(Quote) >= 0)
            {
                int indBegin = Result.IndexOf(Quote);
                if (indBegin >= 0)
                {
                    int indEnd = Result.IndexOf(Quote, indBegin + 1);
                    if (indEnd >= 0)
                    {
                        int lReplace = indEnd - indBegin + 1;
                        string strReplace = RepeatSymbol("*", lReplace);
                        ReplaceSubstring(ref Result, indBegin, strReplace);
                        //Result =
                        //    Result.Replace(Result.Substring(indBegin, indEnd - indBegin + 1), "");
                    }
                    else
                    {
                        //closing bracket is not found, so replace only beginning quote
                        ReplaceSubstring(ref Result, indBegin, "*");
                        break;
                    }
                }
            }
            return Result;
        }

        /// <summary>
        /// The procedure replaces content found between
        ///  /* and */ to content of the same length consisted of
        ///  repeated symbol *
        /// </summary>
        /// <param name="indBeginLine"></param>
        /// <param name="indEndLine"></param>
        /// <param name="indexScriptBlockEnd"></param>
        /// <param name="indLeftCommentBracket"></param>
        /// <param name="indLastLineOfComment"></param>
        /// <param name="indRightCommentBracket"></param>
        private void ReplaceBracketComments(int indBeginLine,
            int indEndLine, int indexScriptBlockEnd,
            int indLeftCommentBracket,
            out int indLastLineOfComment, out int indRightCommentBracket)
        {
            const string LeftCommentBracket = "/*";
            const string RightCommentBracket = "*/";
            
            int LineIndexOfClosingBracket = 0;
            int IndexOfClosingBracket = 0;
            bool RightCommentBracketIsFound =
              FindClosingBracket(indBeginLine, indEndLine,
               indLeftCommentBracket, LeftCommentBracket, RightCommentBracket,
               out  LineIndexOfClosingBracket, out  IndexOfClosingBracket);
            if (!RightCommentBracketIsFound)
            //if closing bracket */ is not found
            // then replace content of all lines in this script block
            {
                LineIndexOfClosingBracket = indEndLine;
                IndexOfClosingBracket = indexScriptBlockEnd;
            }
            //loop for replacing of commentaries
            int StartIndex = 0;
            int CountToExamine = 0;
            string strReplace = "";
            string newline = "";
            for (int i = indBeginLine; i <= LineIndexOfClosingBracket; i++)
            {
                newline = AllDocument[i].ToString();
                CountToExamine = GetCountToExamine(newline, i, indBeginLine,
                    indEndLine, indLeftCommentBracket, IndexOfClosingBracket, out StartIndex);
                strReplace = RepeatSymbol("*", CountToExamine);
                ReplaceSubstring(ref newline, StartIndex, strReplace);
                AllDocument[i] = newline;

            }
            indLastLineOfComment = LineIndexOfClosingBracket;
            indRightCommentBracket = IndexOfClosingBracket;




        }

        /// <summary>
        /// search for block
        /// function functionname(parameters){
        /// </summary>
        /// <param name="IndexOfLine">
        ///  index of line from which make search in array AllDocument
        /// </param>
        /// <returns>
        ///  index of 
        /// </returns>
        private bool FindJScriptFunction(int IndexOfFirstLineForSearch,
            int IndexOfFunctionWord,
            int IndexOfLastLineForSearch,
            out int IndexOfFirstLineOfFunction,
            out int IndexOfLastLineOfFunction,
            out int IndexOfEndFunction,
            out string FunctionName)
        {
            //Algorithm
            // 1.Beginning from line number IndexOfLine
            //  1.1.find word "function" 
            //  1.2.find first ( and function name between "function" and "("
            //  1.3.find first )
            // if all this found in some line than save its number in IndexOfFirstLineOfFunction
            // 2. Check that the next not space symbol is {
            // 3.Find closing } and save its index of line in IndexOfLastLineOfFunction

            const string FunctionWord = "function";
            bool Result = true;
            IndexOfFirstLineOfFunction = -1;
            IndexOfLastLineOfFunction = 0;
            IndexOfEndFunction = 0;
            FunctionName = "";
            string line = "";
            int IndexOfFunctionWordFounded = 0;
            int StartIndex = IndexOfFunctionWord;
            //1.1.search word "function"
            for (int i = IndexOfFirstLineForSearch;
                i <= IndexOfLastLineForSearch; i++)
            {
                line = AllDocument[i].ToString();
                IndexOfFunctionWordFounded = line.IndexOf(FunctionWord, StartIndex);
                //we don't want to count functions without name
                //such as mt[i].getMinimumResolution = function() {return 6;}
                if (IndexOfFunctionWordFounded >= 0 && IsFunctionNameExist(line))
                {
                    IndexOfFirstLineOfFunction = i;
                    break;
                }
                else
                    StartIndex = 0;

            }
            if (IndexOfFirstLineOfFunction == -1)
            {
                return false;
            }
            //here we are if a "function is found
            //1.2.find first ( and function name between "function" and "("
            //we suppose that function <function name>( are found in the same string
            line = AllDocument[IndexOfFirstLineOfFunction].ToString();
            int indOpenRoundBracket = line.IndexOf("(", IndexOfFunctionWordFounded + 8);
            if (indOpenRoundBracket >= 0)
            {
                FunctionName = line.Substring(IndexOfFunctionWordFounded + 9,
                               indOpenRoundBracket - (IndexOfFunctionWordFounded + 9));
                FunctionName = FunctionName.Trim();
            }
            else return false;

            //  1.3.find first )
            int indClosingRoundBracket = line.IndexOf(")", indOpenRoundBracket + 1);
            if (indClosingRoundBracket < 0) return false;
            // 2. Check that the next not space symbol is {
            StartIndex = indClosingRoundBracket + 1;
            int indOpenBrace = -1;
            for (int i = IndexOfFirstLineOfFunction;
                i <= IndexOfLastLineForSearch; i++)
            {
                line = AllDocument[i].ToString();
                indOpenBrace = line.IndexOf("{", StartIndex);
                if (indOpenBrace >= 0)
                    break;
                else
                {
                    StartIndex = 0;
                }

            }
            if (indOpenBrace < 0) return false;
            //here we also could check that all symbols between 
            // ")" and "{" are spaces or commentaries;

            // 3.Find closing }

            string OpenBracket = "{";
            string ClosingBracket = "}";
            Result = FindClosingBracket(IndexOfFirstLineOfFunction,
                IndexOfLastLineForSearch,
             indOpenBrace, OpenBracket, ClosingBracket,
             out  IndexOfLastLineOfFunction, out  IndexOfEndFunction);
            return Result;
        }

        private bool IsFunctionNameExist(string lineString)
        {
            lineString=lineString.ToLower().Trim();
            int startIndex=lineString.IndexOf("function");
            startIndex =startIndex+8;//8 = length of function
            int endIndex=lineString.IndexOf("(");
            if (startIndex >= endIndex)
                return false;
            string functionName = lineString.Substring(startIndex, endIndex - startIndex);
            functionName= functionName.Trim();
            if (functionName.Length > 0)
                return true;
            else
                return false;
            
        }
        /// <summary>
        /// search for blocks
        ///  a) function functionname(parameters)
        ///     end function
        ///  b) sub functionname(parameters)
        ///     end sub
        /// </summary>
        /// <param name="IndexOfLine">
        ///  index of line from which make search in array AllDocument
        /// </param>
        /// <returns>
        ///  index of 
        /// </returns>
        private bool FindVBScriptFunction(int IndexOfFirstLineForSearch,
            int IndexOfFunctionWord,
            int IndexOfLastLineForSearch,
            out int IndexOfFirstLineOfFunction,
            out int IndexOfLastLineOfFunction,
            out int IndexOfEndFunction,
            out string FunctionName)
        {
            //Algorithm
            // 1.Beginning from line number IndexOfFirstLineForSearch
            //  1.1.find word "function"  or "sub"
            //  1.2.at the same line find the first token after space:
            //  and before ( or * :
            //   it will be function name
            //  
            // if all this found in some line than save its number in IndexOfFirstLineOfFunction

            // 2.search in the following lines "end function" or "end sub"
            //and save its index  in IndexOfLastLineOfFunction

            const string FunctionWord = "function";
            const string SubWord = "sub";
            const string EndFunctionWord = "end function";
            const string EndSubWord = "end sub";
            const string ExitWord = "exit";
            int FunctionCase = 1; //1 - for function, 2 for sub
            bool Result = false;
            IndexOfFirstLineOfFunction = -1;
            IndexOfLastLineOfFunction = 0;
            IndexOfEndFunction = 0;
            FunctionName = "";
            string line = "";
            int IndexOfFunctionWordFounded = 0;
            int IndexOfSubWordFounded = 0;
            int StartIndex = IndexOfFunctionWord;
            int indFunctionName = 0;
            
            //1.1.search word "function" or "sub"
            for (int i = IndexOfFirstLineForSearch;
                i <= IndexOfLastLineForSearch; i++)
            {
                line = AllDocument[i].ToString();
                IndexOfFunctionWordFounded = line.ToLower().IndexOf(FunctionWord, StartIndex);
                //if IndexOfFunctionWordFounded check that it is not exit function
                if (IndexOfFunctionWordFounded >= 0)
                {
                    if (line.ToLower().IndexOf(ExitWord) ==
                        IndexOfFunctionWordFounded - 5)
                        IndexOfFunctionWordFounded = 0; //cancel this function
                }

                IndexOfSubWordFounded = line.ToLower().IndexOf(SubWord, StartIndex);
                if (IndexOfSubWordFounded >= 0)
                {
                    if (line.ToLower().IndexOf(ExitWord) ==
                        IndexOfSubWordFounded - 5)
                        IndexOfSubWordFounded = 0; //cancel this sub
                }
                if (IndexOfFunctionWordFounded >= 0)
                {
                    IndexOfFirstLineOfFunction = i;
                    FunctionCase = 1;
                    indFunctionName = IndexOfFunctionWordFounded + 8;
                    break;
                }
                else
                    if (IndexOfSubWordFounded >= 0)
                    {
                        IndexOfFirstLineOfFunction = i;
                        FunctionCase = 2;
                        indFunctionName = IndexOfSubWordFounded + 3;
                        IndexOfFunctionWordFounded = IndexOfSubWordFounded;
                        break;
                    }
                    else
                        StartIndex = 0;

            }
            if (IndexOfFirstLineOfFunction == -1)
            {
                return false;
            }
            //here we are if a "function"is found or "sub" is found

            //1.2. find function name in the same line

            line = AllDocument[IndexOfFirstLineOfFunction].ToString();
            FunctionName = line.Substring(indFunctionName, line.Length - indFunctionName);
            FunctionName = FunctionName.Trim();
            //*************find first not letter of digit symbol
            int lFunctionName = FunctionName.Length;
            char[] symbols = FunctionName.ToCharArray();

            for (int i = 0; i < lFunctionName; i++)
            {
                if (!Char.IsLetterOrDigit(symbols[i]) && !(symbols[i] == '_'))
                {
                    if (i > 0)
                        FunctionName = FunctionName.Substring(0, i);
                    break;
                }
            }

            //****************
            string ClosingBracket = EndFunctionWord;
            if (FunctionCase == 2) ClosingBracket = EndSubWord;

            for (int i = IndexOfFirstLineOfFunction; i <= IndexOfLastLineForSearch; i++)
            {
                line = AllDocument[i].ToString();
                if (line.ToLower().IndexOf(ClosingBracket, 0) >= 0)
                {
                    IndexOfLastLineOfFunction = i;
                    IndexOfEndFunction = line.ToLower().IndexOf(ClosingBracket, 0);
                    Result = true;
                    break;
                }
            }
            return Result;
        }
        /// <summary>
        /// The procedure finds ClosingBracket for OpenBracket
        /// For example in string
        /// { {} {{{}}} } the lasr brace is closing for the first one
        /// It is supposed that the first line contains OpenBracket
        ///  The function should find a line where corresponding
        ///  closing bracket is found. 
        /// The function works in match case
        /// </summary>
        /// <param name="IndexOfFirstLineForSearch">
        ///  index of line where is found an OpenBracket
        /// </param>
        /// <param name="IndexOfOpenBracket">
        ///   index of OpenBracket in this line
        /// </param>
        /// <param name="OpenBracket">
        ///  any string (for example "{" or "sub")
        /// </param>
        /// <param name="ClosingBracket">
        ///  any string string not equal to the OpenBracket
        ///  (for example "}" or "end sub"
        /// </param>
        /// <param name="LineIndexOfClosingBracket">
        ///  Output:index of line containing corresponding closing bracket
        /// </param>
        /// <param name="IndexOfClosingBracket">
        /// Output: index of this bracket in the line
        /// </param>
        /// <returns></returns>
        private bool FindClosingBracket(int IndexOfFirstLineForSearch,
            int IndexOfLastLineForSearch,
            int IndexOfOpenBracket, string OpenBracket, string ClosingBracket,
            out int LineIndexOfClosingBracket, out int IndexOfClosingBracket)
        {

            // BracketsIndex is incremented by one when OpenBracket is found
            // and decremented by one when ClosingBracket is found
            int BracketsIndex = 0;

            int IndexCurrentOpenBracket = 0;
            int IndexCurrentClosingBracket = 0;

            bool RightBracketIsFound = false;
            LineIndexOfClosingBracket = 0;
            IndexOfClosingBracket = 0;

            string line = "";
            int LineStartIndex = IndexOfOpenBracket;
            int i = IndexOfFirstLineForSearch;
            bool OpenBracketIsFound = false;
            while (i <= IndexOfLastLineForSearch)
            {

                line = AllDocument[i].ToString();
                IndexCurrentOpenBracket = line.IndexOf(OpenBracket, LineStartIndex);
                IndexCurrentClosingBracket = line.IndexOf(ClosingBracket, LineStartIndex);
                //only one of four cases is possible
                //1 case : brackets are not found
                if (IndexCurrentOpenBracket < 0 && IndexCurrentClosingBracket < 0)
                {
                    LineStartIndex = 0;
                    //go to the next line
                    i += 1;
                }
                //2 case: closing bracket is first and open not found
                if (IndexCurrentOpenBracket < 0 && IndexCurrentClosingBracket >= 0)
                {
                    BracketsIndex -= 1;
                    LineStartIndex = IndexCurrentClosingBracket + 1;
                }
                //3 case: OpenBracket is first and closing not found
                if (IndexCurrentClosingBracket < 0 && IndexCurrentOpenBracket >= 0)
                {
                    OpenBracketIsFound = true;
                    BracketsIndex += 1;
                    LineStartIndex = IndexCurrentOpenBracket + 1;
                }
                //4 case both brackets are found
                if (IndexCurrentClosingBracket >= 0 && IndexCurrentOpenBracket >= 0)
                {
                    OpenBracketIsFound = true;
                    if (IndexCurrentClosingBracket < IndexCurrentOpenBracket)
                    {
                        BracketsIndex -= 1;
                        LineStartIndex = IndexCurrentClosingBracket + 1;
                    }
                    else
                    {
                        BracketsIndex += 1;
                        LineStartIndex = IndexCurrentOpenBracket + 1;
                    }
                }
                //stop search if BracketsIndex=0
                if (OpenBracketIsFound)
                    if (BracketsIndex == 0)
                    {
                        RightBracketIsFound = true;
                        LineIndexOfClosingBracket = i;
                        IndexOfClosingBracket = IndexCurrentClosingBracket;
                        return RightBracketIsFound;
                    }

            }
            return RightBracketIsFound;//false


        }

        /// <summary>
        /// Add a new "function" node to ParentNode in ScriptStructure
        ///  and assign it value and attribute "line"
        /// </summary>
        /// <param name="ParentNode"></param>
        /// <param name="FunctionName">
        ///  value of new node 
        /// </param>
        /// <param name="IndexOfFirstLineOfFunction">
        ///  value that should be added to attribute "line" of the new node
        /// </param>
        private void AddFunctionNode(XmlNode ParentNode, string FunctionName,
            int IndexOfFirstLineOfFunction)
        {
            XmlNode node = ScriptStructure.CreateElement("function");
            node.InnerText = FunctionName;
            XmlAttribute attr = ScriptStructure.CreateAttribute("line");
            attr.Value = IndexOfFirstLineOfFunction.ToString();
            node.Attributes.Append(attr);
            ParentNode.AppendChild(node);
        }

        /// <summary>
        /// Let some sequence of symbols should be found in a line
        /// that is inside some block of lines
        /// Given line of the document, its index, 
        ///  index of the first line of a block and the last one,
        ///  index of beginning symbol in the first and end symbol in the last line,
        /// this function returns length of string that should be searched
        /// in the current line, and the start index of this string
        /// </summary>
        /// <param name="line"></param>
        /// <param name="LineIndex"></param>
        /// <param name="BeginLineIndex"></param>
        /// <param name="EndLineIndex"></param>
        /// <param name="indBeginSearch"></param>
        /// <param name="indEndSearch"></param>
        /// <param name="StartIndex"></param>
        /// <returns></returns>
        private int GetCountToExamine(string line,
            int LineIndex, int BeginLineIndex, int EndLineIndex,
            int indBeginSearch, int indEndSearch, out int StartIndex)
        {

            int EndIndex = 0;

            if (LineIndex == BeginLineIndex)
                StartIndex = indBeginSearch;
            else
                StartIndex = 0;

            if (LineIndex == EndLineIndex)
                EndIndex = indEndSearch;
            else
                EndIndex = line.Length - 1;

            return EndIndex - StartIndex + 1;
        }

        /// <summary>
        /// The procedure runs over nodes of ScriptStructure
        ///  and builds according to them nodes of control trvScript
        /// For each SCRIPT block a script node  will be added 
        ///  and for each function of the script a child node will be added
        ///  to the script node
        /// </summary>
        private void BuildTree()
        {
            //clear treeview
            trvScript.Nodes.Clear();
            //define root;
            XmlNode root = ScriptStructure.DocumentElement;
            XmlNodeList ScriptInfoNodes = root.ChildNodes;
            XmlNodeList FunctionInfoNodes = null;
            TreeNode ScriptNode = null;
            TreeNode FunctionNode = null;
            //create root of treeview
            TreeNode TreeRoot = new TreeNode("Client script");
            trvScript.Nodes.Add(TreeRoot);
            TreeRoot.ImageIndex = 2;
            string ScriptName = "";
            string FunctionName = "";
            string LineIndex = "";
            foreach (XmlNode ScriptInfoNode in ScriptInfoNodes)
            {
                ScriptName = ScriptInfoNode.Attributes.GetNamedItem("language").InnerText;
                ScriptNode = new TreeNode(ScriptName);
                TreeRoot.Nodes.Add(ScriptNode);

                ScriptNode.ImageIndex = 2;
                ScriptNode.SelectedImageIndex = 2;
                LineIndex = ScriptInfoNode.Attributes.GetNamedItem("line").InnerText;
                ScriptNode.Tag = LineIndex;


                //add functions nodes

                FunctionInfoNodes = ScriptInfoNode.ChildNodes;
                foreach (XmlNode FunctionInfoNode in FunctionInfoNodes)
                {
                    FunctionName = FunctionInfoNode.InnerText;
                    FunctionNode = new TreeNode(FunctionName);
                    ScriptNode.Nodes.Add(FunctionNode);
                    FunctionNode.ImageIndex = 0;
                    FunctionNode.SelectedImageIndex = 0;
                
                    LineIndex = FunctionInfoNode.Attributes.GetNamedItem("line").InnerText;
                    FunctionNode.Tag = LineIndex;
                }

            }
            TreeNodeCollection children = trvScript.Nodes;
            foreach (TreeNode node in children)
            {
                node.Expand();
            }
           //trvScript.ExpandAll();
            
        }
        /// <summary>
        /// The procedure runs over nodes of ScriptStructure
        ///  and builds according to them a sorted dictionary with
        ///  function name as key. Then it runs over the dictionary
        ///  for each function a node will be added
        ///  to the root node
        /// </summary>
        private void BuildSortedTree()
        {
         
            //define root;
            XmlNode root = ScriptStructure.DocumentElement;
            XmlNodeList ScriptInfoNodes = root.ChildNodes;
            XmlNodeList FunctionInfoNodes = null;
            string FunctionName = "";
            string LineIndex = "";
  
            string ScriptName = "";

            //clear treeview
            trvScript.Nodes.Clear();
            //create root of treeview
            TreeNode TreeRoot = new TreeNode(_FileFullName);
           
            trvScript.Nodes.Add(TreeRoot);
            TreeRoot.ImageIndex = 2;
            TreeNode FunctionNode = null;
            TreeNode BlockNode = null;
            foreach (XmlNode ScriptInfoNode in ScriptInfoNodes)
            {
                if (ScriptInfoNode.ChildNodes.Count == 0)
                {
                    ScriptName = ScriptInfoNode.Attributes["language"].InnerText;
                    LineIndex = ScriptInfoNode.Attributes["line"].InnerText;
                    BlockNode = new TreeNode(ScriptName);
                    BlockNode.ImageIndex = 2;
                    BlockNode.Tag = LineIndex;
                    BlockNode.SelectedImageIndex = 2;
                    TreeRoot.Nodes.Add(BlockNode);
                    
                }
                else //Script Block has functions
                {
                    ScriptName = ScriptInfoNode.Attributes["language"].InnerText;
                    LineIndex = ScriptInfoNode.Attributes["line"].InnerText;

                    FunctionInfoNodes = ScriptInfoNode.ChildNodes;

                    BlockNode = new TreeNode(ScriptName);
                    BlockNode.ImageIndex = 3;
                    BlockNode.Tag = LineIndex;
                    BlockNode.SelectedImageIndex = 3;
                    TreeRoot.Nodes.Add(BlockNode);                  

                    foreach (XmlNode FunctionInfoNode in FunctionInfoNodes)
                    {
                        FunctionName = FunctionInfoNode.InnerText;
                        LineIndex = FunctionInfoNode.Attributes.GetNamedItem("line").InnerText;
                        FunctionNode = new TreeNode(FunctionName);
                        BlockNode.Nodes.Add(FunctionNode);
                        FunctionNode.ImageIndex = 0;
                        FunctionNode.SelectedImageIndex = 0;
                        FunctionNode.Tag = LineIndex;

                    }
                }
            }
                      
            TreeNodeCollection children = trvScript.Nodes;
            foreach (TreeNode node in children)
            {
                node.Expand();
            }
        }

        /// <summary>
        /// When a node in the tree control is selected the function
        ///  makes focus on a function corresponding to the node
        /// </summary>
        /// <param name="node"></param>
        private void GotoFunctionDef(TreeNode node)
        {
            if (node.Tag != null)
            {
                if(node.Tag.GetType().Equals(typeof(CodeNode)))
                {
                    CodeNode codeNode = (CodeNode) this.trvScript.SelectedNode.Tag;
                    int LineIndex = codeNode.StartLine;
                    TextDocument txtDoc =
                   (TextDocument)m_dte.ActiveDocument.Object("TextDocument");
                    txtDoc.Selection.GotoLine(LineIndex, true);
				
                }
                else
                {
                int LineIndex = int.Parse(node.Tag.ToString()) + 1;
                TextDocument txtDoc =
                   (TextDocument)m_dte.ActiveDocument.Object("TextDocument");
                txtDoc.Selection.GotoLine(LineIndex, true);
                }
                
            }
        }
        #region Common string functions
        private string RepeatSymbol(string symbol, int n)
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < n; i++)
            {
                sb.Append(symbol);
            }
            return sb.ToString();
        }

        private void ReplaceSubstring(ref string line, int StartIndex,
            string strReplace)
        {
            line = line.Remove(StartIndex, strReplace.Length);
            line = line.Insert(StartIndex, strReplace);
        }
        #endregion

        #endregion

        #region Events handlers
        private void ToolWindowControl_Load(object sender, EventArgs e)
        {
            try
            {

                BuildFunctionsList();


            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                MessageBox.Show(ex.StackTrace);
                trvScript.Nodes.Clear();
            }
            finally
            {


            }
        }

        private void ToolWindowControl_Resize(object sender, EventArgs e)
        {
            trvScript.Width = this.Width;
            trvScript.Height = this.Height;
        }

        /// <summary>
        ///gets selected node, gets from it the line property
        /// and selects corresponding line in the active document
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void trvScript_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            GotoFunctionDef(e.Node);

        }
      

        /// <summary>
        /// Gets selected node from the tree control
        ///  and focuses on corresponding line in the document
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void contextMenuStrip1_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (e.ClickedItem.Name == tsGotoDef.Name)
            // MessageBox.Show("GotoDef");
            {
                TreeNode node = trvScript.SelectedNode;
                if (node != null)
                    GotoFunctionDef(node);
            }
        }

        /// <summary>
        /// Sorting of the tree by function names
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsalphabetically_Click(object sender, EventArgs e)
        {
            BuildSortedTree();
        }

        /// <summary>
        /// Sorting of the tree by physical order of functions
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsByDeclaration_Click(object sender, EventArgs e)
        {
            BuildTree();
        }
        #endregion





    }
}
